//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <event.h>
#include "usbser.h"
#include <nu_api.h>

//#define USBSYCN_DEBUG
#ifdef USBSYCN_DEBUG
#define DBG(x...) kprintf(x)
#else
#define DBG(x...)
#endif

CUsbsync *g_pUsbsync = NULL;

extern "C" void Usbsync_Callback(char *pbuf, int len, int SyncEvent)
{
    int enqn;

    //DBG("Usbsync_Callback is %s\n",pbuf);
    DBG("Usbsync_Callback:len=%d,g_pUsbsync->m_IsSync=%d\n",(unsigned int)len,g_pUsbsync->m_IsSync);
    enqn = UsbRBuf_Enqueue(&g_pUsbsync->m_buffer, (UInt8 *)pbuf, len);
    DBG("enqn=%d\n",enqn);
    if (g_pUsbsync->m_IsSync) {
        //g_pUsbsync->m_SyncRd.Pulse();
        //DBG("notice read\n");
        nu_notifyDeviceCaller((Int32)&(g_pUsbsync->m_event), SIGNALED);
    }
    if (g_pUsbsync->m_IsPostEvent) {
        if (enqn == len) {
            g_pUsbsync->m_EventToPost = UVCP_RX_CHAR;
        }
        else {
            g_pUsbsync->m_EventToPost = UVCP_ENQ_FAIL;
        }
    }
    if (NULL != g_pUsbsync->m_pRbufEvent) {
        //DzNotifyEventObjByIsr(g_pUsbsync->m_pRbufEvent, g_pUsbsync->m_EventToPost);
        nu_notifyDeviceCaller((Int32)&(((CEvent *)(g_pUsbsync->m_pRbufEvent))->m_kevent), (Int32)(g_pUsbsync->m_EventToPost));
    }

    return;
}

ECode CUsbsync::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    UInt32 cnt, readn;
    UInt8 *pdata;

    //m_Lock.Lock(&wr);
    if (pBuffer == NULL) {
        //m_Lock.Unlock();
        return E_INVALID_ARGUMENT;
    }

    pdata = (UInt8 *)pBuffer->GetPayload();
    readn = 0;
    cnt = bytesToRead;
    DBG("m_IsSync=%x, cnt=%d\n", m_IsSync, cnt);
    if (m_IsSync) {
        while (cnt) {
            readn = UsbRBuf_Dequeue(&m_buffer, pdata, cnt);
            DBG("readn=%d\n", readn);
            cnt -= readn;
            pdata += readn;
            if (0 == readn) {
                WaitResult wrt;
                EventState state;
                //m_SyncRd.Wait(&m_Lock, &wrt);
                m_event.Wait(&wrt, &state);
            }
        }
        cnt = bytesToRead;
    }
    else {
        cnt = UsbRBuf_Dequeue(&m_buffer, pdata, cnt);
    }
    cnt = cnt * (m_buffer.elemtype);
    pBuffer->SetUsed(cnt);
    DBG("Read is %s, size=%d\n",pdata, cnt);
    //m_Lock.Unlock();

    return NOERROR;
}

ECode CUsbsync::Write(
        /* [in] */ Int64 offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32* pBytesWritten,
        /* [out] */ IEvent** completionEvent)
{
    char *pBuffer;
    unsigned long size, num;
    unsigned int ret = 0;
    ECode ec = NOERROR;

    //m_Lock.Lock(&wr);
    if (!pBytesWritten) return E_INVALID_ARGUMENT;
    pBuffer = (char *)buffer.GetPayload();
    size = buffer.GetUsed();
    //DBG("Write is %s, size=%d\n",pBuffer, size);

    while (size) {
        num = size;
        ret = nu_diolink_write(pBuffer, (unsigned int *)&num);
        if (ret) {
            ec = E_NOT_WRITER;
            kprintf("Usbsync::Write() error\n");
            *((unsigned int *)pBytesWritten) = buffer.GetUsed() - size;
            //m_Lock.Unlock();
            return ec;
        }
        else {
            size -= num;
            pBuffer += num;
        }
    }

    *((unsigned int *)pBytesWritten) = buffer.GetUsed() - size;
    //m_Lock.Unlock();
    return ec;
}

ECode CUsbsync::Control(
        /* [in] */ Handle32 controlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf* pOutBuffer,
        /* [out] */ IEvent** completionEvent)
{
    ECode ec = NOERROR;
    switch(controlCode) {
        case UCDC_INIT:
            break;

        case UCDC_STOP:
            Usbsync_Stop();
            break;

        case UCDC_CLR_BUF:
            UsbRBuf_Reset(&m_buffer);
            break;

        case UCDC_WAKE:
            ec = Usbsync_Wake();
            if (FAILED(ec)) {
                UnInit();
            }
            else {
                DBG("Emulate High Speed Serial Port ... !\n");
            }
            break;

        case UCDC_SET_SYNC:
            m_IsSync = TRUE;
            break;

        case UCDC_SET_ASYNC:
            m_IsSync = FALSE;
            break;

        case UCDC_SET_PRIF:
            break;

        case UDCD_WAIT_EVENT:
            if (FALSE == m_IsPostEvent) {
                m_IsPostEvent = TRUE;
            }
            if (NULL != m_pRbufEvent) {
                m_pRbufEvent->Release();
                m_pRbufEvent = NULL;
            }
            if (NULL == completionEvent) {
                ec = E_INVALID_ARGUMENT;
                break;
            }
            DBG("DzCreateEventObj:m_pRbufEvent\n");
            DzCreateEventObj(FALSE, UNSIGNALED, &m_pRbufEvent);
            m_pRbufEvent->AddRef();
            *completionEvent = m_pRbufEvent;
            break;

        case UDCD_SET_EVENT:
            if (NULL != m_pRbufEvent) {
                DBG("m_pRbufEvent\n");
                m_IsPostEvent = FALSE;
                UsbRBuf_Reset(&m_buffer);
                m_pRbufEvent->Notify(UVCP_SIGNAL_OUT);
                m_pRbufEvent->Release();
                m_pRbufEvent = NULL;
            }
            break;

        default:
            kprintf("USB CDC DEVICE UnKnown OpCode %08x\n", (unsigned int)controlCode);
            ec = E_INVALID_ARGUMENT;
    }

    return ec;
}

int CUsbsync::Initialize()
{
    ECode ec = NOERROR;
    m_IsSync = FALSE;
    m_IsInit = TRUE;
    ec = UsbRBuf_Init(&m_buffer, sizeof(UInt8));

    return ec;
}

void CUsbsync::UnInit()
{
    m_IsSync = FALSE;
    UsbRBuf_UnInit(&m_buffer);
    m_IsInit = FALSE;
}

CUsbsync::CUsbsync()
{
    m_EventToPost = 0;
    m_IsPostEvent = TRUE;
    m_pRbufEvent = NULL;
    //m_bRead = FALSE;
    m_IsInit = FALSE;

    Usbsync_Wake();
//    m_IsSync = TRUE;
}

CUsbsync::~CUsbsync()
{
    UnInit();
}

void CUsbsync::Usbsync_Stop()
{
    return;
}

ECode CUsbsync::Usbsync_Wake()
{
    if (FALSE == m_IsInit) {
        DBG("Usbsync_Wake:m_IsInit=%d\n",m_IsInit);
        return Initialize();
        //Initialize();
    }
    nu_UsbsyncEnable(2);

    return NOERROR;
}

EXTERN IDeviceDriver * CDECL CreateUsbsync(uint_t uDeviceNo, void *pvParameter)
{
    g_pUsbsync = new CUsbsync;
    if (!g_pUsbsync) {
        DRV_EXPRESSION(ERRORLEVEL, kprintf("not enough memory\n"));
        return NULL;
    }

    nu_initUsbsync(&Usbsync_Callback);

    DBG("Init Usb sync Succeed!\n");
    return g_pUsbsync;
}
